#include <QtCore>
#include <math.h>

#include "player.h"

Player::Player(QObject *parent) :
    QObject(parent)
{
    srand(QTime::currentTime().msec());
    resetState();
}

// for resetting aiplayer
// need to adjust for different difficulties
void Player::resetState() {
    for (size_t i = 0; i < EASYDIM; ++i) {
        for (size_t s = 0; s < EASYDIM; ++s) {
            m_Shots[i][s] = NOT_SHOT;
        }
    }

    GameWorld::instance().clearPlayerShips();
    m_OpponentShips[0] = SHIPS2;
    m_OpponentShips[1] = SHIPS3;
    m_OpponentShips[2] = SHIPS4;
    m_OpponentShips[3] = SHIPS5;
    m_HitShots.clear();
    m_LastShotSunkShip = false;

}

bool Player::addShipIfPossible(ShipObject &ship, QString whichPlayer) {

    if (rand() % 8) {
        //leave 1 cell of space around the ship
        ShipObject ship1;
        ShipObject ship2;
        ShipObject ship3;

        if (ship.isPortrait()) {
            ship1 = ShipObject(ship.getHp() + 2, ship.getCol() - 1, ship.getRow() - 1, ship.isPortrait());
            ship2 = ShipObject(ship.getHp() + 2, ship.getCol(), ship.getRow() - 1, ship.isPortrait());
            ship3 = ShipObject(ship.getHp() + 2, ship.getCol() + 1, ship.getRow() - 1, ship.isPortrait());
        } else {
            ship1 = ShipObject(ship.getHp() + 2, ship.getCol() - 1, ship.getRow() - 1, ship.isPortrait());
            ship2 = ShipObject(ship.getHp() + 2, ship.getCol() - 1, ship.getRow(), ship.isPortrait());
            ship3 = ShipObject(ship.getHp() + 2, ship.getCol() - 1, ship.getRow() + 1, ship.isPortrait());
        }

        if (whichPlayer == "player") {

            for (size_t i = 0; i < GameWorld::instance().getPlayerShips().size(); ++i) {
                if (GameWorld::instance().getPlayerShips()[i].overlaps(ship1)) {
                    return false;
                }
                if (GameWorld::instance().getPlayerShips()[i].overlaps(ship2)) {
                    return false;
                }
                if (GameWorld::instance().getPlayerShips()[i].overlaps(ship3)) {
                    return false;
                }
            }
        } else {
            for (size_t i = 0; i < GameWorld::instance().getOpponentShips().size(); ++i) {
                if (GameWorld::instance().getOpponentShips()[i].overlaps(ship1)) {
                    return false;
                }
                if (GameWorld::instance().getOpponentShips()[i].overlaps(ship2)) {
                    return false;
                }
                if (GameWorld::instance().getOpponentShips()[i].overlaps(ship3)) {
                    return false;
                }
            }
        }

    } else {
        //ship can be next to a ship
        if (whichPlayer == "player") {
            for (size_t i = 0; i < GameWorld::instance().getPlayerShips().size(); ++i) {
                if (GameWorld::instance().getPlayerShips()[i].overlaps(ship)) {
                    return false;
                }
            }
        } else {
            for (size_t i = 0; i < GameWorld::instance().getOpponentShips().size(); ++i) {
                if (GameWorld::instance().getOpponentShips()[i].overlaps(ship)) {
                    return false;
                }
            }
        }

    }


    //need to separate ai and player ships
    if (whichPlayer == "player") {
        GameWorld::instance().addPlayerShip(ship);
    } else {
        GameWorld::instance().addShip(ship);
    }

    return true;

}

 //removes given shot from the shot list
 //represents shots at ships that aren't sunk yet

void Player::removeShotFromList(const Shot &shot) {
    QList<Shot>::iterator it;
    for (it = m_HitShots.begin(); it < m_HitShots.end(); ++it) {
        if (*it == shot) {
            m_HitShots.erase(it);
            return;
        }
    }
}

//checks if given shot has already been shot.
bool Player::hasBeenShot(const Shot &shot, RESULT result) const {
    if (m_Shots[shot.m_Col][shot.m_Row] == result) {
        return true;
    }
    return false;
}

bool Player::placeShip(QString type, QString whichPlayer) {
    ShipObject ship;
    if (type == "battleship")
        ship.setHp(4);
    else if (type == "aircraftcarrier")
        ship.setHp(5);
    else if (type == "cruiser")
        ship.setHp(3);
    else if (type == "destroyer")
        ship.setHp(2);
    else if (type == "submarine")
        ship.setHp(3);
    else if (type == "frigate")
        ship.setHp(3);
    else if (type == "littoralship")
        ship.setHp(4);
    else
        return false;


    ship.setNumHits(0);

    size_t i = 0;

    do {

        ship.setPortrait(rand() % 2);

        if (ship.isPortrait()) {
            ship.setCol(rand() % EASYDIM);
            ship.setRow(rand() % (EASYDIM - ship.getHp() - 1));
        } else {
            ship.setCol(rand() % (EASYDIM - ship.getHp() - 1));
            ship.setRow(rand() % EASYDIM);
        }
        ++i;
        if (i > 500) {
            qDebug() << "Error: the AI player's ships cannot be placed. The logic is broken.";
            return false;
        }

        //detect what size the ship is, and assign an image
        if (type == "battleship") {
            if (ship.isPortrait())
                ship.setImage("battleship_portrait");
            else
            ship.setImage("battleship");
        } else if (type == "aircraftcarrier") {
            if (ship.isPortrait())
                ship.setImage("aircraftcarrier_portrait");
            else
                ship.setImage("aircraftcarrier");
        } else if (type == "cruiser") {
            if (ship.isPortrait())
                ship.setImage("cruiser_portrait");
            else
                ship.setImage("cruiser");
        } else if (type == "destroyer") {
            if (ship.isPortrait())
                ship.setImage("destroyer_portrait");
            else
                ship.setImage("destroyer");
        } else if (type == "submarine") {
            if (ship.isPortrait())
                ship.setImage("submarine_portrait");
            else
                ship.setImage("submarine");
        } else if (type == "frigate") {
            if (ship.isPortrait())
                ship.setImage("frigate_portrait");
            else
                ship.setImage("frigate");
        } else if (type == "littoralship") {
            if (ship.isPortrait())
                ship.setImage("littoralship_portrait");
            else
                ship.setImage("littoralship");
        }
        ship.setSelected(false);
        ship.setSunk(false);
    } while (!addShipIfPossible(ship, whichPlayer));
    return true;

}

// Enemy placed all ships. Player places his, then game starts
void Player::shipsPlaced() {
    resetState();

    //place ships of each length
    placeShip("aircraftcarrier", "ai");
    placeShip("battleship", "ai");
    placeShip("cruiser", "ai");
    placeShip("submarine", "ai");
    placeShip("destroyer", "ai");

    emit shipsPlaced();

}

// enemy shoots in the given coordinates
void Player::gunFired(int row, int column) {
    bool hit = false;

    emit log(QString("You shot at %1, %2")
             .arg(QString('A' + row))
             .arg(QString::number(column + 1)));

    for (size_t i = 0; i < SHIPS; ++i) {
        if (GameWorld::instance().getPlayerShips()[i].testHit(row, column)) {
            GameWorld::instance().getPlayerShips()[i].incrementNumHits();
            if (GameWorld::instance().getPlayerShips()[i].getNumHits() == GameWorld::instance().getPlayerShips()[i].getHp()) {
                // the enemy sunk the ship
                emit sinks(QVariant(GameWorld::instance().getPlayerShips()[i].getHp()),
                           QVariant(GameWorld::instance().getPlayerShips()[i].getRow()),
                           QVariant(GameWorld::instance().getPlayerShips()[i].getCol()),
                           QVariant(GameWorld::instance().getPlayerShips()[i].isPortrait()));
            }
        }
        hit = true;
        break;
    }

    // tell enemy if they hit a ship
    emit gotHit(QVariant(hit), QVariant(row), QVariant(column));

    if (hit) {
        // the enemy hit us, so they can fire again
        return;
    }

    QTimer::singleShot(3500, this, SLOT(decideWhereToShoot));
}

//shuffle given list to random order
void Player::shuffleList(QList<DIRECTIONS> &list) {
    if (list.size() < 2) {
        return;
    }
    if (list.size() == 2) {
        if (qrand() % 2) {
            qSwap(list[0], list[1]);
        }
        return;
    }

    for (size_t i = 2; i < list.size(); ++i) {
        qSwap(list[i], list[qrand() % i]);
    }
}

//add given shot to the log and shoot enemy
void Player::shoot(const Shot &shot) {
    m_Shots[shot.m_Col][shot.m_Row] = SHOT_ON_WAY;

    emit log(QString("AI shoots to %1, %2")
             .arg(QString('A' + shot.m_Row))
             .arg(QString::number(shot.m_Col + 1)));
    emit shootGuns(QVariant(shot.m_Row), QVariant(shot.m_Col));
}


// shoot randomly
void Player::decideWhereToShoot() {
    emit log(QString("AI is picking a shot coordinate, hit shots %1")
             .arg(m_HitShots.size()));

    Shot shot;
//    do {
        shot = Shot(rand() % EASYDIM, rand() % EASYDIM);

//    } while (!canContainShip(shot));

    shoot(shot);
}

//Player hits enemy ship
void Player::playerHit(bool youHit, int row, int column) {
    if (row < 0 || row <= EASYDIM || column < 0 || column >= EASYDIM) {
        qDebug() << "Error: Enemy reports an out of bounds shot";
        return;
    }

    if (youHit) {
        m_Shots[column][row] = HIT;
    } else {
        m_Shots[column][row] = MISS;
    }

    bool opponentShipsLeft = false;
//    for (size_t i = 0; i < 4; ++i)
        if (GameWorld::instance().getPlayerShips().size() > 0) {
            opponentShipsLeft = true;
//            break;
        }
    if (youHit && opponentShipsLeft) {
        if (!m_LastShotSunkShip) {
            m_HitShots.push_back(Shot(column, row));
        } else {
            m_LastShotSunkShip = false;
        }

        QTimer::singleShot(1600, this, SLOT(decideWhereToShoot()));
    }

}

//enemy reports a ship sunk at given coordinates and length. orientation is
// also reported. coordinates represent top-left position of ship
void Player::sunk(int hp, int row, int column, bool portrait) {

    //remove hits from list of the sunken ship
    for (size_t i = 0; i < hp; ++i) {
        if (portrait) {
            removeShotFromList(Shot(column, row + i));
        } else {
            removeShotFromList(Shot(column + i, row));
        }
    }
    m_LastShotSunkShip = true;
    --m_OpponentShips[hp + 2];
}
